package org.processor;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

public class RuntimeBeanReflector implements BeanReflector {

	private Map<String, BeanClass> cache;
	
	public RuntimeBeanReflector() {
		cache = new Hashtable<String, BeanClass>();
	}
	
	@Override
	public boolean supportsComments() {
		return false;
	}
	
	@Override
	public BeanClass reflect(String qualifiedClassName) throws Exception {
		Class<?> foundClass = Class.forName(qualifiedClassName);
		
		return reflectClass(foundClass);
	}
	
	private BeanClass reflectClass(Class<?> foundClass) throws IntrospectionException {
		Class<?> superClassClass = Object.class;
		BeanClass superClass = null;
		if (foundClass.isInterface()) {
			Class<?>[] interfaces = foundClass.getInterfaces();
			if (interfaces != null && interfaces.length > 0) {
				if (interfaces.length > 1) {
					throw new IntrospectionException("Class "+foundClass.getName()+" could not be resolved. Multiple extending interfaces are not allowed for bean classes.");
				}
				superClass = reflectClass(interfaces[0]);
				superClassClass = interfaces[0];
			}
		} else if (superClass != null){
			superClassClass = foundClass.getSuperclass();
			superClass = reflectClass(foundClass.getSuperclass());
		}
		
		String packageName = foundClass.getPackage() != null ? foundClass.getPackage().getName() : null;
		BeanClass clazz = new BeanClass(foundClass, foundClass.getName(), 
				foundClass.getSimpleName(), packageName, foundClass.isPrimitive(), packageName == null);
		
		clazz.setSuperClass(superClass);
		
		List<BeanProperty> properties = new ArrayList<BeanProperty>();
		BeanInfo beanInfo = Introspector.getBeanInfo(foundClass, superClassClass);
		PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor propertyDescriptor : descriptors) {
			BeanProperty property = new BeanProperty();
			property.setName(propertyDescriptor.getName());
			property.setType(createTypeReference(propertyDescriptor.getPropertyType()));
			property.setEditable(propertyDescriptor.getWriteMethod() != null);
			properties.add(property);
		}
		clazz.setProperties(properties);
		
		return clazz;
	}
	
	
	private TypeReference createTypeReference(Class<?> clazz) {
		String packageName = clazz.getPackage() != null ? clazz.getPackage().getName() : null;
		TypeReference typeReference = new TypeReference(clazz.getName(), 
				clazz.getSimpleName(), packageName, clazz.isPrimitive(), packageName == null);
		return typeReference;
	}

}
